﻿using System;
using System.Text;

namespace Sencha.Serialization
{
	[System.Diagnostics.DebuggerDisplay("{Length}")]
	public sealed class JsonString : IEquatable<JsonString>
	{
		private string m_jsonString;
		private StringBuilder m_jsonStringBuilder;
		private bool m_isPrimitive;

		public bool IsPrimitive { get { return m_isPrimitive; } }
		public int Length { get { return m_jsonString != null ? m_jsonString.Length : m_jsonStringBuilder.Length; } }

		public JsonString()
		{
			m_jsonString = string.Empty;
		}
		public JsonString(string jsonString)
		{
			if (jsonString == null)
				throw new ArgumentNullException("jsonString");
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			m_jsonString = jsonString;
		}
		public JsonString(StringBuilder jsonString)
		{
			if (jsonString == null)
				throw new ArgumentNullException("jsonString");
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			m_jsonStringBuilder = jsonString;
		}
		public JsonString(char[] jsonString, int index, int length)
		{
			if (jsonString == null)
				throw new ArgumentNullException("jsonString");
			if (length < 0)
				throw new ArgumentOutOfRangeException("length");
			if (index < 0 || index + length > jsonString.Length)
				throw new ArgumentOutOfRangeException("index");
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			m_jsonStringBuilder = new StringBuilder(length + 1);
			m_jsonStringBuilder.Append(jsonString, index, length);
		}
		public JsonString(string jsonString, int index, int length)
		{
			if (jsonString == null)
				throw new ArgumentNullException("jsonString");
			if (length < 0)
				throw new ArgumentOutOfRangeException("length");
			if (index < 0 || index + length > jsonString.Length)
				throw new ArgumentOutOfRangeException("index");

			m_jsonStringBuilder = new StringBuilder(length + 1);
			m_jsonStringBuilder.Append(jsonString, index, length);
		}
		public JsonString(string[] jsonStrings)
		{
			if (jsonStrings == null)
				throw new ArgumentNullException("jsonStrings");
			System.Diagnostics.Contracts.Contract.ForAll(jsonStrings, s => s != null);
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			var length = 0;
			for (int i = 0; i < jsonStrings.Length; i++)
			{
				if (jsonStrings[i] == null)
					jsonStrings[i] = string.Empty;

				length += jsonStrings[i].Length;
			}

			m_jsonStringBuilder = new StringBuilder(length + 1);
			for (int i = 0; i < jsonStrings.Length; i++)
				m_jsonStringBuilder.Append(jsonStrings[i]);
		}

		private void EnsureStringValue()
		{
			if (m_jsonStringBuilder != null)
				m_jsonString = m_jsonStringBuilder.ToString();
			m_jsonStringBuilder = null;
		}

		public override int GetHashCode()
		{
			if (m_jsonString == null && m_jsonStringBuilder == null)
				throw new InvalidOperationException();
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			return m_jsonStringBuilder != null ? m_jsonStringBuilder.GetHashCode() : m_jsonString.GetHashCode();
		}
		public override bool Equals(object obj)
		{
			return this.Equals(obj as JsonString);
		}
		public bool Equals(JsonString jsString)
		{
			if (m_jsonString == null && m_jsonStringBuilder == null)
				throw new InvalidOperationException();
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			if (Object.ReferenceEquals(this, jsString))
				return true;

			if (jsString.m_jsonString == null && this.m_jsonString == null)
				return jsString.m_jsonStringBuilder.Equals(this.m_jsonStringBuilder);
			else
				return this.ToString().Equals(jsString.ToString());
		}

		public IJsonReader ToJsonReader(ISerializationContext context = null)
		{
			if (m_jsonString == null && m_jsonStringBuilder == null)
				throw new InvalidOperationException();
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			if (m_jsonString != null)
				return new JsonStringReader(m_jsonString, context);
			else
				return new JsonStringBuilderReader(m_jsonStringBuilder, context);
		}

		public override string ToString()
		{
			if (m_jsonString == null && m_jsonStringBuilder == null)
				throw new InvalidOperationException();
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			this.EnsureStringValue();

			return m_jsonString.ToString();
		}

		internal static string UnquoteAndUnescape(string stringToUnescape)
		{
			if (stringToUnescape == null)
				throw new ArgumentNullException("stringToUnescape");
			if (stringToUnescape.Length < 2)
				throw new ArgumentException("stringToUnescape");
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			return JsonUtils.UnescapeBuffer(stringToUnescape.ToCharArray(), 1, stringToUnescape.Length - 2);
		}
		public static string Unescape(string stringToUnescape)
		{
			if (stringToUnescape == null)
				throw new ArgumentNullException("stringToUnescape");
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			return JsonUtils.UnescapeBuffer(stringToUnescape.ToCharArray(), 0, stringToUnescape.Length);
		}
		public static string Escape(string stringToEscape)
		{
			if (stringToEscape == null)
				throw new ArgumentNullException("stringToEscape");
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			return JsonUtils.EscapeAndQuote(stringToEscape);
		}
	}
}
